use super::{output::Link, TCCLI_BIN};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

#[derive(Debug)]
pub struct BinaryVersionCmd<'a> {
  name: &'a str,
  version_args: &'a str,
  help_link: Option<Link<'a>>,
}

impl<'a> BinaryVersionCmd<'a> {
  pub const fn new(name: &'a str, version_args: &'a str, help_link: Option<Link<'a>>) -> Self {
    BinaryVersionCmd {
      name,
      version_args,
      help_link,
    }
  }

  pub fn exec(&self) -> bool {
    if cfg!(target_os = "windows") {
      panic!("no impl for windows!")
    } else {
      let output = Command::new(self.name).arg(self.version_args).output();
      match output {
        Ok(output) => {
          return output.status.success();
        }
        _ => {
          let help_link = self
            .help_link
            .as_ref()
            .map_or("none".to_owned(), |v| v.to_string());
          eprintln!(
            "Not found tccli!!\nPlease install tccli first. \nSee:{}",
            help_link
          );
          return false;
        }
      }
    }
  }
}

#[derive(Debug)]
pub struct ShellCmd<'a> {
  text: &'a str,
  err_msg: &'a str,
  help_link: Option<Link<'a>>,
}

impl<'a> ShellCmd<'a> {
  pub const fn new(text: &'a str, help_link: Option<Link<'a>>, err_msg: &'a str) -> Self {
    ShellCmd {
      text,
      err_msg,
      help_link,
    }
  }

  pub fn exec(&self) -> bool {
    if cfg!(target_os = "windows") {
      panic!("no impl for windows!")
    } else {
      let status = Command::new("sh")
        .arg("-c")
        .arg(self.text)
        .stdout(Stdio::null())
        .status()
        .expect(self.err_msg);

      let success = status.success();
      if !success {
        eprintln!("{}", self.err_msg);
        let help_link = self
          .help_link
          .as_ref()
          .map_or("none".to_owned(), |v| v.to_string());
        eprintln!("See:{}", help_link)
      }

      success
    }
  }
}

#[derive(Debug)]
pub struct UploadSSLCertificateCmd<'a> {
  cert: &'a str,
  key: &'a str,
  help_link: Option<Link<'a>>,
}

impl<'a> UploadSSLCertificateCmd<'a> {
  pub const fn new(cert: &'a str, key: &'a str, help_link: Option<Link<'a>>) -> Self {
    UploadSSLCertificateCmd {
      cert,
      key,
      help_link,
    }
  }

  pub fn exec(&self) -> std::io::Result<&'static str> {
    if cfg!(target_os = "windows") {
      panic!("no impl for windows!")
    } else {
      let output = Command::new(TCCLI_BIN)
        .arg("ssl")
        .arg("UploadCertificate")
        .arg("--cli-unfold-argument")
        .arg("--CertificatePublicKey")
        .arg(self.cert)
        .arg("--CertificatePrivateKey")
        .arg(self.key)
        .output();
      match output {
        Ok(output) => {
          println!(
            "upload output:{}",
            String::from_utf8(output.stdout).unwrap()
          );
          println!("upload success:{}", String::from_utf8(output.stderr).unwrap());
          return Ok("ok");
        }
        Err(err) => {
          let help_link = self
            .help_link
            .as_ref()
            .map_or("none".to_owned(), |v| v.to_string());
          eprintln!("sync ssl cert error!\ndetail:{}\nSee:{}", err, help_link);
          return Err(err)
        }
      }
    }
  }
}

pub fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
  let p = path_user_input.as_ref();
  if !p.starts_with("~") {
    return Some(p.to_path_buf());
  }
  if p == Path::new("~") {
    return dirs::home_dir();
  }
  dirs::home_dir().map(|mut h| {
    if h == Path::new("/") {
      // Corner case: `h` root directory;
      // don't prepend extra `/`, just drop the tilde.
      p.strip_prefix("~").unwrap().to_path_buf()
    } else {
      h.push(p.strip_prefix("~/").unwrap());
      h
    }
  })
}
